home *** CD-ROM | disk | FTP | other *** search
/ Workbench Add-On / Workbench Add-On - Volume 1.iso / Text / Show / Less / less-252 / screen.c < prev    next >
C/C++ Source or Header  |  1995-01-05  |  20KB  |  1,064 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994  Mark Nudelman
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice in the documentation and/or other materials provided with 
  12.  *    the distribution.
  13.  *
  14.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
  15.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  17.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
  18.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  19.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
  20.  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
  21.  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  22.  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
  23.  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 
  24.  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25.  */
  26.  
  27.  
  28. /*
  29.  * Routines which deal with the characteristics of the terminal.
  30.  * Uses termcap to be as terminal-independent as possible.
  31.  *
  32.  * {{ Maybe someday this should be rewritten to use curses or terminfo. }}
  33.  */
  34.  
  35. #include "less.h"
  36. #include "cmd.h"
  37.  
  38. #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
  39. #include <termios.h>
  40. #if HAVE_SYS_IOCTL_H && !defined(TIOCGWINSZ)
  41. #include <sys/ioctl.h>
  42. #endif
  43. #else
  44. #if HAVE_TERMIO_H
  45. #include <termio.h>
  46. #else
  47. #include <sgtty.h>
  48. #if HAVE_SYS_IOCTL_H && (defined(TIOCGWINSZ) || defined(TCGETA) || defined(TIOCGETP) || defined(WIOCGETD))
  49. #include <sys/ioctl.h>
  50. #endif
  51. #endif
  52. #endif
  53. #if HAVE_TERMCAP_H
  54. #include <termcap.h>
  55. #endif
  56.  
  57. #ifndef TIOCGWINSZ
  58. /*
  59.  * For the Unix PC (ATT 7300 & 3B1):
  60.  * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
  61.  * whether to include sys/window.h.  Use SIGPHONE from sys/signal.h instead.
  62.  */
  63. #include <sys/signal.h>
  64. #ifdef SIGPHONE
  65. #include <sys/window.h>
  66. #endif
  67. #endif
  68.  
  69. #if HAVE_SYS_STREAM_H
  70. #include <sys/stream.h>
  71. #endif
  72. #if HAVE_SYS_PTEM_H
  73. #include <sys/ptem.h>
  74. #endif
  75.  
  76.  
  77. /*
  78.  * Strings passed to tputs() to do various terminal functions.
  79.  */
  80. static char
  81.     *sc_pad,        /* Pad string */
  82.     *sc_home,        /* Cursor home */
  83.     *sc_addline,        /* Add line, scroll down following lines */
  84.     *sc_lower_left,        /* Cursor to last line, first column */
  85.     *sc_move,        /* General cursor positioning */
  86.     *sc_clear,        /* Clear screen */
  87.     *sc_eol_clear,        /* Clear to end of line */
  88.     *sc_eos_clear,        /* Clear to end of screen */
  89.     *sc_s_in,        /* Enter standout (highlighted) mode */
  90.     *sc_s_out,        /* Exit standout mode */
  91.     *sc_u_in,        /* Enter underline mode */
  92.     *sc_u_out,        /* Exit underline mode */
  93.     *sc_b_in,        /* Enter bold mode */
  94.     *sc_b_out,        /* Exit bold mode */
  95.     *sc_bl_in,        /* Enter blink mode */
  96.     *sc_bl_out,        /* Exit blink mode */
  97.     *sc_visual_bell,    /* Visual bell (flash screen) sequence */
  98.     *sc_backspace,        /* Backspace cursor */
  99.     *sc_s_keypad,        /* Start keypad mode */
  100.     *sc_e_keypad,        /* End keypad mode */
  101.     *sc_init,        /* Startup terminal initialization */
  102.     *sc_deinit;        /* Exit terminal de-initialization */
  103.  
  104. static int init_done = 0;
  105.  
  106. public int auto_wrap;        /* Terminal does \r\n when write past margin */
  107. public int ignaw;        /* Terminal ignores \n immediately after wrap */
  108. public int erase_char, kill_char; /* The user's erase and line-kill chars */
  109. public int sc_width, sc_height;    /* Height & width of screen */
  110. public int bo_s_width, bo_e_width;    /* Printing width of boldface seq */
  111. public int ul_s_width, ul_e_width;    /* Printing width of underline seq */
  112. public int so_s_width, so_e_width;    /* Printing width of standout seq */
  113. public int bl_s_width, bl_e_width;    /* Printing width of blink seq */
  114. public int above_mem, below_mem;    /* Memory retained above/below screen */
  115.  
  116. static char *cheaper();
  117.  
  118. /*
  119.  * These two variables are sometimes defined in,
  120.  * and needed by, the termcap library.
  121.  */
  122. #if MUST_DEFINE_OSPEED
  123. extern short ospeed;    /* Terminal output baud rate */
  124. extern char PC;        /* Pad character */
  125. #endif
  126.  
  127. extern int quiet;        /* If VERY_QUIET, use visual bell for bell */
  128. extern int know_dumb;        /* Don't complain about a dumb terminal */
  129. extern int back_scroll;
  130. extern int swindow;
  131. extern int no_init;
  132. extern char *tgetstr();
  133. extern char *tgoto();
  134.  
  135.  
  136. /*
  137.  * Change terminal to "raw mode", or restore to "normal" mode.
  138.  * "Raw mode" means 
  139.  *    1. An outstanding read will complete on receipt of a single keystroke.
  140.  *    2. Input is not echoed.  
  141.  *    3. On output, \n is mapped to \r\n.
  142.  *    4. \t is NOT expanded into spaces.
  143.  *    5. Signal-causing characters such as ctrl-C (interrupt),
  144.  *       etc. are NOT disabled.
  145.  * It doesn't matter whether an input \n is mapped to \r, or vice versa.
  146.  */
  147.     public void
  148. raw_mode(on)
  149.     int on;
  150. {
  151.     static int curr_on = 0;
  152.  
  153.     if (on == curr_on)
  154.         return;
  155. #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
  156.     {
  157.     struct termios s;
  158.     static struct termios save_term;
  159.  
  160.     if (on) 
  161.     {
  162.         /*
  163.          * Get terminal modes.
  164.          */
  165.         tcgetattr(2, &s);
  166.  
  167.         /*
  168.          * Save modes and set certain variables dependent on modes.
  169.          */
  170.         save_term = s;
  171. #if HAVE_OSPEED
  172.         switch (cfgetospeed(&s))
  173.         {
  174. #ifdef B0
  175.         case B0: ospeed = 0; break;
  176. #endif
  177. #ifdef B50
  178.         case B50: ospeed = 1; break;
  179. #endif
  180. #ifdef B75
  181.         case B75: ospeed = 2; break;
  182. #endif
  183. #ifdef B110
  184.         case B110: ospeed = 3; break;
  185. #endif
  186. #ifdef B134
  187.         case B134: ospeed = 4; break;
  188. #endif
  189. #ifdef B150
  190.         case B150: ospeed = 5; break;
  191. #endif
  192. #ifdef B200
  193.         case B200: ospeed = 6; break;
  194. #endif
  195. #ifdef B300
  196.         case B300: ospeed = 7; break;
  197. #endif
  198. #ifdef B600
  199.         case B600: ospeed = 8; break;
  200. #endif
  201. #ifdef B1200
  202.         case B1200: ospeed = 9; break;
  203. #endif
  204. #ifdef B1800
  205.         case B1800: ospeed = 10; break;
  206. #endif
  207. #ifdef B2400
  208.         case B2400: ospeed = 11; break;
  209. #endif
  210. #ifdef B4800
  211.         case B4800: ospeed = 12; break;
  212. #endif
  213. #ifdef B9600
  214.         case B9600: ospeed = 13; break;
  215. #endif
  216. #ifdef EXTA
  217.         case EXTA: ospeed = 14; break;
  218. #endif
  219. #ifdef EXTB
  220.         case EXTB: ospeed = 15; break;
  221. #endif
  222. #ifdef B57600
  223.         case B57600: ospeed = 16; break;
  224. #endif
  225. #ifdef B115200
  226.         case B115200: ospeed = 17; break;
  227. #endif
  228.         default: ;
  229.         }
  230. #endif
  231.         erase_char = s.c_cc[VERASE];
  232.         kill_char = s.c_cc[VKILL];
  233.  
  234.         /*
  235.          * Set the modes to the way we want them.
  236.          */
  237.         s.c_lflag &= ~(0
  238. #ifdef ICANON
  239.             | ICANON
  240. #endif
  241. #ifdef ECHO
  242.             | ECHO
  243. #endif
  244. #ifdef ECHOE
  245.             | ECHOE
  246. #endif
  247. #ifdef ECHOK
  248.             | ECHOK
  249. #endif
  250. #if ECHONL
  251.             | ECHONL
  252. #endif
  253.         );
  254.  
  255.         s.c_oflag |= (0
  256. #ifdef XTABS
  257.             | XTABS
  258. #else
  259. #ifdef TAB3
  260.             | TAB3
  261. #else
  262. #ifdef OXTABS
  263.             | OXTABS
  264. #endif
  265. #endif
  266. #endif
  267. #ifdef OPOST
  268.             | OPOST
  269. #endif
  270. #ifdef ONLCR
  271.             | ONLCR
  272. #endif
  273.         );
  274.  
  275.         s.c_oflag &= ~(0
  276. #ifdef ONOEOT
  277.             | ONOEOT
  278. #endif
  279. #ifdef OCRNL
  280.             | OCRNL
  281. #endif
  282. #ifdef ONOCR
  283.             | ONOCR
  284. #endif
  285. #ifdef ONLRET
  286.             | ONLRET
  287. #endif
  288.         );
  289.         s.c_cc[VMIN] = 1;
  290.         s.c_cc[VTIME] = 0;
  291.     } else
  292.     {
  293.         /*
  294.          * Restore saved modes.
  295.          */
  296.         s = save_term;
  297.     }
  298.     tcsetattr(2, TCSADRAIN, &s);
  299.     }
  300. #else
  301. #ifdef TCGETA
  302.     {
  303.     struct termio s;
  304.     static struct termio save_term;
  305.  
  306.     if (on)
  307.     {
  308.         /*
  309.          * Get terminal modes.
  310.          */
  311.         ioctl(2, TCGETA, &s);
  312.  
  313.         /*
  314.          * Save modes and set certain variables dependent on modes.
  315.          */
  316.         save_term = s;
  317. #if HAVE_OSPEED
  318.         ospeed = s.c_cflag & CBAUD;
  319. #endif
  320.         erase_char = s.c_cc[VERASE];
  321.         kill_char = s.c_cc[VKILL];
  322.  
  323.         /*
  324.          * Set the modes to the way we want them.
  325.          */
  326.         s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
  327.         s.c_oflag |=  (OPOST|ONLCR|TAB3);
  328.         s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
  329.         s.c_cc[VMIN] = 1;
  330.         s.c_cc[VTIME] = 0;
  331.     } else
  332.     {
  333.         /*
  334.          * Restore saved modes.
  335.          */
  336.         s = save_term;
  337.     }
  338.     ioctl(2, TCSETAW, &s);
  339.     }
  340. #else
  341.     {
  342.     struct sgttyb s;
  343.     static struct sgttyb save_term;
  344.  
  345.     if (on)
  346.     {
  347.         /*
  348.          * Get terminal modes.
  349.          */
  350.         ioctl(2, TIOCGETP, &s);
  351.  
  352.         /*
  353.          * Save modes and set certain variables dependent on modes.
  354.          */
  355.         save_term = s;
  356. #if HAVE_OSPEED
  357.         ospeed = s.sg_ospeed;
  358. #endif
  359.         erase_char = s.sg_erase;
  360.         kill_char = s.sg_kill;
  361.  
  362.         /*
  363.          * Set the modes to the way we want them.
  364.          */
  365.         s.sg_flags |= CBREAK;
  366.         s.sg_flags &= ~(ECHO|XTABS);
  367.     } else
  368.     {
  369.         /*
  370.          * Restore saved modes.
  371.          */
  372.         s = save_term;
  373.     }
  374.     ioctl(2, TIOCSETN, &s);
  375.     }
  376. #endif
  377. #endif
  378.     curr_on = on;
  379. }
  380.  
  381.     static void
  382. cannot(s)
  383.     char *s;
  384. {
  385.     PARG parg;
  386.  
  387.     if (know_dumb)
  388.         /* 
  389.          * User knows this is a dumb terminal, so don't tell him.
  390.          */
  391.         return;
  392.  
  393.     parg.p_string = s;
  394.     error("WARNING: terminal cannot %s", &parg);
  395. }
  396.  
  397. /*
  398.  * Get size of the output screen.
  399.  */
  400.     public void
  401. scrsize()
  402. {
  403.     register char *s;
  404. #ifdef TIOCGWINSZ
  405.     struct winsize w;
  406. #else
  407. #ifdef WIOCGETD
  408.     struct uwdata w;
  409. #endif
  410. #endif
  411.  
  412. #ifdef TIOCGWINSZ
  413.     if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
  414.         sc_height = w.ws_row;
  415.     else
  416. #else
  417. #ifdef WIOCGETD
  418.     if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
  419.         sc_height = w.uw_height/w.uw_vs;
  420.     else
  421. #endif
  422. #endif
  423.     if ((s = getenv("LINES")) != NULL)
  424.         sc_height = atoi(s);
  425.     else
  426.          sc_height = tgetnum("li");
  427.  
  428.     if (sc_height <= 0)
  429.         sc_height = 24;
  430.  
  431. #ifdef TIOCGWINSZ
  432.      if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
  433.         sc_width = w.ws_col;
  434.     else
  435. #ifdef WIOCGETD
  436.     if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
  437.         sc_width = w.uw_width/w.uw_hs;
  438.     else
  439. #endif
  440. #endif
  441.     if ((s = getenv("COLUMNS")) != NULL)
  442.         sc_width = atoi(s);
  443.     else
  444.          sc_width = tgetnum("co");
  445.  
  446.      if (sc_width <= 0)
  447.           sc_width = 80;
  448. }
  449.  
  450. /*
  451.  * Take care of the "variable" keys.
  452.  * Certain keys send escape sequences which differ on different terminals
  453.  * (such as the arrow keys, INSERT, DELETE, etc.)
  454.  * Construct the commands based on these keys.
  455.  */
  456.     public void
  457. get_editkeys()
  458. {
  459.     char *sp;
  460.     char *s;
  461.     char tbuf[40];
  462.  
  463.     static char kfcmdtable[400];
  464.     int sz_kfcmdtable = 0;
  465.     static char kecmdtable[400];
  466.     int sz_kecmdtable = 0;
  467.  
  468. #define    put_cmd(str,action,tbl,sz) { \
  469.     strcpy(tbl+sz, str);    \
  470.     sz += strlen(str) + 1;    \
  471.     tbl[sz++] = action; }
  472. #define    put_esc_cmd(str,action,tbl,sz) { \
  473.     tbl[sz++] = ESC; \
  474.     put_cmd(str,action,tbl,sz); }
  475.  
  476. #define    put_fcmd(str,action)    put_cmd(str,action,kfcmdtable,sz_kfcmdtable)
  477. #define    put_ecmd(str,action)    put_cmd(str,action,kecmdtable,sz_kecmdtable)
  478. #define    put_esc_fcmd(str,action) put_esc_cmd(str,action,kfcmdtable,sz_kfcmdtable)
  479. #define    put_esc_ecmd(str,action) put_esc_cmd(str,action,kecmdtable,sz_kecmdtable)
  480.  
  481.     /*
  482.      * Look at some interesting keys and see what strings they send.
  483.      * Create commands (both command keys and line-edit keys).
  484.      */
  485.  
  486.     /* RIGHT ARROW */
  487.     sp = tbuf;
  488.     if ((s = tgetstr("kr", &sp)) != NULL)
  489.     {
  490.         put_ecmd(s, EC_RIGHT);
  491.         put_esc_ecmd(s, EC_W_RIGHT);
  492.     }
  493.     
  494.     /* LEFT ARROW */
  495.     sp = tbuf;
  496.     if ((s = tgetstr("kl", &sp)) != NULL)
  497.     {
  498.         put_ecmd(s, EC_LEFT);
  499.         put_esc_ecmd(s, EC_W_LEFT);
  500.     }
  501.     
  502.     /* UP ARROW */
  503.     sp = tbuf;
  504.     if ((s = tgetstr("ku", &sp)) != NULL) 
  505.     {
  506.         put_ecmd(s, EC_UP);
  507.         put_fcmd(s, A_B_LINE);
  508.     }
  509.         
  510.     /* DOWN ARROW */
  511.     sp = tbuf;
  512.     if ((s = tgetstr("kd", &sp)) != NULL) 
  513.     {
  514.         put_ecmd(s, EC_DOWN);
  515.         put_fcmd(s, A_F_LINE);
  516.     }
  517.  
  518.     /* PAGE UP */
  519.     sp = tbuf;
  520.     if ((s = tgetstr("kP", &sp)) != NULL) 
  521.     {
  522.         put_fcmd(s, A_B_SCREEN);
  523.     }
  524.  
  525.     /* PAGE DOWN */
  526.     sp = tbuf;
  527.     if ((s = tgetstr("kN", &sp)) != NULL) 
  528.     {
  529.         put_fcmd(s, A_F_SCREEN);
  530.     }
  531.     
  532.     /* HOME */
  533.     sp = tbuf;
  534.     if ((s = tgetstr("kh", &sp)) != NULL) 
  535.     {
  536.         put_ecmd(s, EC_HOME);
  537.     }
  538.  
  539.     /* END */
  540.     sp = tbuf;
  541.     if ((s = tgetstr("@7", &sp)) != NULL) 
  542.     {
  543.         put_ecmd(s, EC_END);
  544.     }
  545.  
  546.     /* DELETE */
  547.     sp = tbuf;
  548.     if ((s = tgetstr("kD", &sp)) == NULL) 
  549.     {
  550.         /* Use DEL (\177) if no "kD" termcap. */
  551.         tbuf[1] = '\177';
  552.         tbuf[2] = '\0';
  553.         s = tbuf+1;
  554.     }
  555.     put_ecmd(s, EC_DELETE);
  556.     put_esc_ecmd(s, EC_W_DELETE);
  557.         
  558.     /* BACKSPACE */
  559.     tbuf[0] = ESC;
  560.     tbuf[1] = erase_char;
  561.     tbuf[2] = '\0';
  562.     put_ecmd(tbuf, EC_W_BACKSPACE);
  563.  
  564.     /*
  565.      * Register the two tables.
  566.      */
  567.     add_fcmd_table(kfcmdtable, sz_kfcmdtable);
  568.     add_ecmd_table(kecmdtable, sz_kecmdtable);
  569. }
  570.  
  571. /*
  572.  * Get terminal capabilities via termcap.
  573.  */
  574.     public void
  575. get_term()
  576. {
  577.     char *sp;
  578.     register char *t1, *t2;
  579.     register int hard;
  580.     char *term;
  581.     char termbuf[2048];
  582.  
  583.     static char sbuf[1024];
  584.  
  585.     /*
  586.      * Find out what kind of terminal this is.
  587.      */
  588.      if ((term = getenv("TERM")) == NULL)
  589. #ifdef __amigados__
  590.       /* A configured machine default would be more sensible
  591.          for those machines that have a "native terminal".
  592.          -fnf */
  593.         term = "amiga";
  594. #else
  595.          term = "unknown";
  596. #endif
  597.      if (tgetent(termbuf, term) <= 0)
  598.          strcpy(termbuf, "dumb:hc:");
  599.  
  600.      hard = tgetflag("hc");
  601.  
  602.     /*
  603.      * Get size of the screen.
  604.      */
  605.     scrsize();
  606.     pos_init();
  607.  
  608.     auto_wrap = tgetflag("am");
  609.     ignaw = tgetflag("xn");
  610.     above_mem = tgetflag("da");
  611.     below_mem = tgetflag("db");
  612.  
  613.     /*
  614.      * Assumes termcap variable "sg" is the printing width of:
  615.      * the standout sequence, the end standout sequence,
  616.      * the underline sequence, the end underline sequence,
  617.      * the boldface sequence, and the end boldface sequence.
  618.      */
  619.     if ((so_s_width = tgetnum("sg")) < 0)
  620.         so_s_width = 0;
  621.     so_e_width = so_s_width;
  622.  
  623.     bo_s_width = bo_e_width = so_s_width;
  624.     ul_s_width = ul_e_width = so_s_width;
  625.     bl_s_width = bl_e_width = so_s_width;
  626.  
  627.     /*
  628.      * Get various string-valued capabilities.
  629.      */
  630.     sp = sbuf;
  631.  
  632. #if HAVE_OSPEED
  633.     sc_pad = tgetstr("pc", &sp);
  634.     if (sc_pad != NULL)
  635.         PC = *sc_pad;
  636. #endif
  637.  
  638.     sc_s_keypad = tgetstr("ks", &sp);
  639.     if (sc_s_keypad == NULL)
  640.         sc_s_keypad = "";
  641.     sc_e_keypad = tgetstr("ke", &sp);
  642.     if (sc_e_keypad == NULL)
  643.         sc_e_keypad = "";
  644.         
  645.     sc_init = tgetstr("ti", &sp);
  646.     if (sc_init == NULL)
  647.         sc_init = "";
  648.  
  649.     sc_deinit= tgetstr("te", &sp);
  650.     if (sc_deinit == NULL)
  651.         sc_deinit = "";
  652.  
  653.     sc_eol_clear = tgetstr("ce", &sp);
  654.     if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
  655.     {
  656.         cannot("clear to end of line");
  657.         sc_eol_clear = "";
  658.     }
  659.  
  660.     sc_eos_clear = tgetstr("cd", &sp);
  661.     if (below_mem && 
  662.         (hard || sc_eos_clear == NULL || *sc_eos_clear == '\0'))
  663.     {
  664.         cannot("clear to end of screen");
  665.         sc_eol_clear = "";
  666.     }
  667.  
  668.     sc_clear = tgetstr("cl", &sp);
  669.     if (hard || sc_clear == NULL || *sc_clear == '\0')
  670.     {
  671.         cannot("clear screen");
  672.         sc_clear = "\n\n";
  673.     }
  674.  
  675.     sc_move = tgetstr("cm", &sp);
  676.     if (hard || sc_move == NULL || *sc_move == '\0')
  677.     {
  678.         /*
  679.          * This is not an error here, because we don't 
  680.          * always need sc_move.
  681.          * We need it only if we don't have home or lower-left.
  682.          */
  683.         sc_move = "";
  684.     }
  685.  
  686.     sc_s_in = tgetstr("so", &sp);
  687.     if (hard || sc_s_in == NULL)
  688.         sc_s_in = "";
  689.  
  690.     sc_s_out = tgetstr("se", &sp);
  691.     if (hard || sc_s_out == NULL)
  692.         sc_s_out = "";
  693.  
  694.     sc_u_in = tgetstr("us", &sp);
  695.     if (hard || sc_u_in == NULL)
  696.         sc_u_in = sc_s_in;
  697.  
  698.     sc_u_out = tgetstr("ue", &sp);
  699.     if (hard || sc_u_out == NULL)
  700.         sc_u_out = sc_s_out;
  701.  
  702.     sc_b_in = tgetstr("md", &sp);
  703.     if (hard || sc_b_in == NULL)
  704.     {
  705.         sc_b_in = sc_s_in;
  706.         sc_b_out = sc_s_out;
  707.     } else
  708.     {
  709.         sc_b_out = tgetstr("me", &sp);
  710.         if (hard || sc_b_out == NULL)
  711.             sc_b_out = "";
  712.     }
  713.  
  714.     sc_bl_in = tgetstr("mb", &sp);
  715.     if (hard || sc_bl_in == NULL)
  716.     {
  717.         sc_bl_in = sc_s_in;
  718.         sc_bl_out = sc_s_out;
  719.     } else
  720.     {
  721.         sc_bl_out = tgetstr("me", &sp);
  722.         if (hard || sc_bl_out == NULL)
  723.             sc_bl_out = "";
  724.     }
  725.  
  726.     sc_visual_bell = tgetstr("vb", &sp);
  727.     if (hard || sc_visual_bell == NULL)
  728.         sc_visual_bell = "";
  729.  
  730.     if (tgetflag("bs"))
  731.         sc_backspace = "\b";
  732.     else
  733.     {
  734.         sc_backspace = tgetstr("bc", &sp);
  735.         if (sc_backspace == NULL || *sc_backspace == '\0')
  736.             sc_backspace = "\b";
  737.     }
  738.  
  739.     /*
  740.      * Choose between using "ho" and "cm" ("home" and "cursor move")
  741.      * to move the cursor to the upper left corner of the screen.
  742.      */
  743.     t1 = tgetstr("ho", &sp);
  744.     if (hard || t1 == NULL)
  745.         t1 = "";
  746.     if (*sc_move == '\0')
  747.         t2 = "";
  748.     else
  749.     {
  750.         strcpy(sp, tgoto(sc_move, 0, 0));
  751.         t2 = sp;
  752.         sp += strlen(sp) + 1;
  753.     }
  754.     sc_home = cheaper(t1, t2, "home cursor", "|\b^");
  755.  
  756.     /*
  757.      * Choose between using "ll" and "cm"  ("lower left" and "cursor move")
  758.      * to move the cursor to the lower left corner of the screen.
  759.      */
  760.     t1 = tgetstr("ll", &sp);
  761.     if (hard || t1 == NULL)
  762.         t1 = "";
  763.     if (*sc_move == '\0')
  764.         t2 = "";
  765.     else
  766.     {
  767.         strcpy(sp, tgoto(sc_move, 0, sc_height-1));
  768.         t2 = sp;
  769.         sp += strlen(sp) + 1;
  770.     }
  771.     sc_lower_left = cheaper(t1, t2,
  772.         "move cursor to lower left of screen", "\r");
  773.  
  774.     /*
  775.      * Choose between using "al" or "sr" ("add line" or "scroll reverse")
  776.      * to add a line at the top of the screen.
  777.      */
  778.     t1 = tgetstr("al", &sp);
  779.     if (hard || t1 == NULL)
  780.         t1 = "";
  781.     t2 = tgetstr("sr", &sp);
  782.     if (hard || t2 == NULL)
  783.         t2 = "";
  784.     if (above_mem)
  785.         sc_addline = t1;
  786.     else
  787.         sc_addline = cheaper(t1, t2, "scroll backwards", "");
  788.     if (*sc_addline == '\0')
  789.     {
  790.         /*
  791.          * Force repaint on any backward movement.
  792.          */
  793.         back_scroll = 0;
  794.     }
  795. }
  796.  
  797. /*
  798.  * Return the cost of displaying a termcap string.
  799.  * We use the trick of calling tputs, but as a char printing function
  800.  * we give it inc_costcount, which just increments "costcount".
  801.  * This tells us how many chars would be printed by using this string.
  802.  * {{ Couldn't we just use strlen? }}
  803.  */
  804. static int costcount;
  805.  
  806. /*ARGSUSED*/
  807.     static int
  808. inc_costcount(c)
  809.     int c;
  810. {
  811.     costcount++;
  812.     return (c);
  813. }
  814.  
  815.     static int
  816. cost(t)
  817.     char *t;
  818. {
  819.     costcount = 0;
  820.     tputs(t, sc_height, inc_costcount);
  821.     return (costcount);
  822. }
  823.  
  824. /*
  825.  * Return the "best" of the two given termcap strings.
  826.  * The best, if both exist, is the one with the lower 
  827.  * cost (see cost() function).
  828.  */
  829.     static char *
  830. cheaper(t1, t2, doit, def)
  831.     char *t1, *t2;
  832.     char *doit;
  833.     char *def;
  834. {
  835.     if (*t1 == '\0' && *t2 == '\0')
  836.     {
  837.         cannot(doit);
  838.         return (def);
  839.     }
  840.     if (*t1 == '\0')
  841.         return (t2);
  842.     if (*t2 == '\0')
  843.         return (t1);
  844.     if (cost(t1) < cost(t2))
  845.         return (t1);
  846.     return (t2);
  847. }
  848.  
  849.  
  850. /*
  851.  * Below are the functions which perform all the 
  852.  * terminal-specific screen manipulation.
  853.  */
  854.  
  855.  
  856. /*
  857.  * Initialize terminal
  858.  */
  859.     public void
  860. init()
  861. {
  862.     if (no_init)
  863.         return;
  864.     tputs(sc_init, sc_height, putchr);
  865.     tputs(sc_s_keypad, sc_height, putchr);
  866.     init_done = 1;
  867. }
  868.  
  869. /*
  870.  * Deinitialize terminal
  871.  */
  872.     public void
  873. deinit()
  874. {
  875.     if (no_init)
  876.         return;
  877.     if (!init_done)
  878.         return;
  879.     tputs(sc_e_keypad, sc_height, putchr);
  880.     tputs(sc_deinit, sc_height, putchr);
  881.     init_done = 0;
  882. }
  883.  
  884. /*
  885.  * Home cursor (move to upper left corner of screen).
  886.  */
  887.     public void
  888. home()
  889. {
  890.     tputs(sc_home, 1, putchr);
  891. }
  892.  
  893. /*
  894.  * Add a blank line (called with cursor at home).
  895.  * Should scroll the display down.
  896.  */
  897.     public void
  898. add_line()
  899. {
  900.     tputs(sc_addline, sc_height, putchr);
  901. }
  902.  
  903. /*
  904.  * Move cursor to lower left corner of screen.
  905.  */
  906.     public void
  907. lower_left()
  908. {
  909.     tputs(sc_lower_left, 1, putchr);
  910. }
  911.  
  912. /*
  913.  * Ring the terminal bell.
  914.  */
  915.     public void
  916. bell()
  917. {
  918.     if (quiet == VERY_QUIET)
  919.         vbell();
  920.     else
  921.         putchr('\7');
  922. }
  923.  
  924. /*
  925.  * Output the "visual bell", if there is one.
  926.  */
  927.     public void
  928. vbell()
  929. {
  930.     if (*sc_visual_bell == '\0')
  931.         return;
  932.     tputs(sc_visual_bell, sc_height, putchr);
  933. }
  934.  
  935. /*
  936.  * Clear the screen.
  937.  */
  938.     public void
  939. clear()
  940. {
  941.     tputs(sc_clear, sc_height, putchr);
  942. }
  943.  
  944. /*
  945.  * Clear from the cursor to the end of the cursor's line.
  946.  * {{ This must not move the cursor. }}
  947.  */
  948.     public void
  949. clear_eol()
  950. {
  951.     tputs(sc_eol_clear, 1, putchr);
  952. }
  953.  
  954. /*
  955.  * Clear the bottom line of the display.
  956.  * Leave the cursor at the beginning of the bottom line.
  957.  */
  958.     public void
  959. clear_bot()
  960. {
  961.     lower_left();
  962.     if (below_mem)
  963.         tputs(sc_eos_clear, 1, putchr);
  964.     else
  965.         tputs(sc_eol_clear, 1, putchr);
  966. }
  967.  
  968. /*
  969.  * Begin "standout" (bold, underline, or whatever).
  970.  */
  971.     public void
  972. so_enter()
  973. {
  974.     tputs(sc_s_in, 1, putchr);
  975. }
  976.  
  977. /*
  978.  * End "standout".
  979.  */
  980.     public void
  981. so_exit()
  982. {
  983.     tputs(sc_s_out, 1, putchr);
  984. }
  985.  
  986. /*
  987.  * Begin "underline" (hopefully real underlining, 
  988.  * otherwise whatever the terminal provides).
  989.  */
  990.     public void
  991. ul_enter()
  992. {
  993.     tputs(sc_u_in, 1, putchr);
  994. }
  995.  
  996. /*
  997.  * End "underline".
  998.  */
  999.     public void
  1000. ul_exit()
  1001. {
  1002.     tputs(sc_u_out, 1, putchr);
  1003. }
  1004.  
  1005. /*
  1006.  * Begin "bold"
  1007.  */
  1008.     public void
  1009. bo_enter()
  1010. {
  1011.     tputs(sc_b_in, 1, putchr);
  1012. }
  1013.  
  1014. /*
  1015.  * End "bold".
  1016.  */
  1017.     public void
  1018. bo_exit()
  1019. {
  1020.     tputs(sc_b_out, 1, putchr);
  1021. }
  1022.  
  1023. /*
  1024.  * Begin "blink"
  1025.  */
  1026.     public void
  1027. bl_enter()
  1028. {
  1029.     tputs(sc_bl_in, 1, putchr);
  1030. }
  1031.  
  1032. /*
  1033.  * End "blink".
  1034.  */
  1035.     public void
  1036. bl_exit()
  1037. {
  1038.     tputs(sc_bl_out, 1, putchr);
  1039. }
  1040.  
  1041. /*
  1042.  * Erase the character to the left of the cursor 
  1043.  * and move the cursor left.
  1044.  */
  1045.     public void
  1046. backspace()
  1047. {
  1048.     /* 
  1049.      * Try to erase the previous character by overstriking with a space.
  1050.      */
  1051.     tputs(sc_backspace, 1, putchr);
  1052.     putchr(' ');
  1053.     tputs(sc_backspace, 1, putchr);
  1054. }
  1055.  
  1056. /*
  1057.  * Output a plain backspace, without erasing the previous char.
  1058.  */
  1059.     public void
  1060. putbs()
  1061. {
  1062.     tputs(sc_backspace, 1, putchr);
  1063. }
  1064.